package com.ding.config;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.management.remote.JMXAuthenticator;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXPrincipal;
import javax.security.auth.Subject;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.NotificationListenerBean;
import org.springframework.jmx.export.annotation.AnnotationMBeanExporter;
import org.springframework.jmx.support.ConnectorServerFactoryBean;
import org.springframework.remoting.rmi.RmiRegistryFactoryBean;

import com.ding.listener.MyServiceListener;
import com.ding.service.MyService;

@Configuration
public class JmxConfig {
	@Bean		//Springboot 能够自动生成 AnnotationMBeanExporter,但是自动生成的没有装配通知监听器的逻辑
	public AnnotationMBeanExporter annotationMBeanExporter(NotificationListenerBean[] listenerBeans) {
		AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();
		exporter.setNotificationListeners(listenerBeans);
		return exporter;
	}
		
	/*
	 * 通知的发布与监听采用了发布/订阅模式
	 */
	
	@Bean	//监听器包装器
	public NotificationListenerBean notificationListenerBean(MyServiceListener listener,MyService service) {
		NotificationListenerBean listenerBean = new NotificationListenerBean();
		listenerBean.setNotificationListener(listener);			//监听器
		listenerBean.setMappedObjectName("Test:name=myservice");	//所监听的MBean
		listenerBean.setHandback(service);						//为监听器提交某个对象
		return listenerBean;
	}

	

/*		
 	导出远程MBean服务
 		1.采用以下方式可以实现暴露(首先创建一个Rmi的注册表,然后将MBean服务绑定到Rmi注册表).
 		2.采用Java原生方式进行暴露(这种方式默认的服务名称为"jmxrmi"):	项目启动前添加如下vm自变量
				-Dcom.sun.management.jmxremote.rmi.port=8888					#端口号
				-Dcom.sun.management.jmxremote.ssl=false 						#是否使用ssl 验证，通常为false
				-Dcom.sun.management.jmxremote.authenticate=true 				#是否需要校验连接用户
				-Dcom.sun.management.jmxremote.password.file=<jmxremote.password文件路径> 	#  用户文件(右键->安全 设置设置文件的所有者为单个用户)
				-Dcom.sun.management.jmxremote.access.file=<jmxremote.access文件路径>			#用户权限配置
						*/	 							
 	
 	@Bean
	public RmiRegistryFactoryBean registryFactoryBean() {//创建rmi注册表
		RmiRegistryFactoryBean bean = new RmiRegistryFactoryBean();
		bean.setPort(8888);
		return bean;
	}
	@Bean
	public ConnectorServerFactoryBean connectorServerFactoryBean() {//将服务绑定到rmi注册表
		ConnectorServerFactoryBean bean = new ConnectorServerFactoryBean();
		bean.setServiceUrl("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi");
		
		//开启安全认证.也可以通过设置 jmx.remote.x.[password/access].file (密码文件以及权限文件)来开启
		Map<String, Object> envConf = new HashMap<>();
		envConf.put(JMXConnectorServer.AUTHENTICATOR, new MyAuthenticator("admain", "****"));
		bean.setEnvironmentMap(envConf);
		
		return bean;
	}
	
	private class MyAuthenticator implements JMXAuthenticator	{
		String username;
		String password;
		public MyAuthenticator(String username,String password) {
			this.password=password;
			this.username=username;
		}
		@Override
		public Subject authenticate(Object credentials) {
			String[] sCredentials = (String[]) credentials;
			if (sCredentials==null || sCredentials.length != 2) {
                System.err.println("认证失败");
            }
            String u = sCredentials[0];
            String p = sCredentials[1];
            if (u.equals(username) && p.equals(password)) {
                Set<JMXPrincipal> principals = new HashSet<>();
                principals.add(new JMXPrincipal(username));
                return new Subject(false, principals, Collections.EMPTY_SET,Collections.EMPTY_SET);
            }
            System.out.println("认证失败!");
			return null;
		}
	}

}
